from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from lib.share import ShareDriver, ShareTicket_Info
import re
import time


class login_12306:
    def __init__(self, user, pw):
        self.user = user
        self.pw = pw

    # 滑块验证
    def Signin_By_Slider(self):
        chrome_options = Options()
        chrome_options.add_argument(r'--user-data-dir=C:\Users\ZCY\AppData\Local\Google\Chrome\User Data')
        chrome_options.add_argument("--disable-web-security")
        # 保持浏览器打开
        chrome_options.add_experimental_option("detach", True)
        driver = webdriver.Chrome(
            executable_path=r'C:\Users\ZCY\Desktop\code\爬虫\venv\Lib\site-packages\selenium\chromedriver.exe',
            chrome_options=chrome_options)
        driver.get("https://kyfw.12306.cn/otn/resources/login.html")
        # noinspection PyBroadException
        try:
            # 输入账号、密码、登录
            input_user = driver.find_element(By.ID, "J-userName")
            input_password = driver.find_element(By.ID, "J-password")
            input_user.send_keys(self.user)
            input_password.send_keys(self.pw)
            time.sleep(1)
            driver.find_element(By.ID, "J-login").click()
            time.sleep(3)
        except Exception:
            # 登录网页失败
            driver.close()
            return {"state": "登录网页失败", "content": "Null"}
        # 滑块验证
        # noinspection PyBroadException
        try:
            slider_check = driver.find_element(By.XPATH, "//*[@id='nc_1_n1z']")
            # 去除特征识别
            script = 'Object.defineProperty(navigator, "webdriver", {get: () => false,});'
            driver.execute_script(script)
            # 滑动滑块，向右鼠标拖动300像素
            ActionChains(driver, duration=500).click_and_hold(slider_check).perform()
            ActionChains(driver, duration=500).move_by_offset(300, 0).perform()
            ActionChains(driver, duration=500).release().perform()
            time.sleep(3)
            ShareDriver.driver = driver
            ShareDriver.login_user = BeautifulSoup(driver.page_source, "html.parser") \
                .select('#J-header-logout > a.txt-primary.menu-nav-my-hd')[0].get_text()
            return {"state": "登录成功", "content": "sucess"}
        except Exception:
            driver.close()
            return {"state": "登录失败", "content": "滑块验证失败"}

    # 短信验证，获取验证码
    def Get_CheckCode(self, idcard):
        chrome_options = Options()
        chrome_options.add_argument(r'--user-data-dir=C:\Users\ZCY\AppData\Local\Google\Chrome\User Data')
        chrome_options.add_argument("--disable-web-security")
        # 保持浏览器打开
        chrome_options.add_experimental_option("detach", True)
        driver = webdriver.Chrome(
            executable_path=r'C:\Users\ZCY\Desktop\code\爬虫\venv\Lib\site-packages\selenium\chromedriver.exe',
            chrome_options=chrome_options)
        driver.get("https://kyfw.12306.cn/otn/resources/login.html")

        # noinspection PyBroadException
        try:
            # 输入账号、密码、登录
            input_user = driver.find_element(By.ID, "J-userName")
            input_password = driver.find_element(By.ID, "J-password")
            input_user.send_keys(self.user)
            input_password.send_keys(self.pw)
            time.sleep(1)
            driver.find_element(By.ID, "J-login").click()
            time.sleep(1)
            message_check = driver.find_element(By.XPATH, "//*[@id='verification']/li[2]/a")
            message_check.click()
            id_check_input = driver.find_element(By.ID, "id_card")
            # 输入身份证后四位
            id_check_input.send_keys(idcard)
            # 验证身份
            if len(idcard) == 4:
                id_check_codesend = driver.find_element(By.XPATH, "//*[@id='verification_code']")
                id_check_codesend.click()
                time.sleep(0.5)
                # 确认身份证后四位验证成功
                check_statecode = BeautifulSoup(driver.page_source, "html.parser").select("#message")
                # print(check_statecode)
                if re.search(r'成功', check_statecode[0].get_text()):
                    get_checkcode = driver.find_element(By.XPATH, "//*[@id='verification_code']")
                    get_checkcode.click()
                    ShareDriver.driver = driver
                    return {"state": "验证成功", "content": "sucess"}
                else:
                    driver.close()
                    return {"state": "验证失败", "content": check_statecode[0].get_text()}
            else:
                driver.close()
                return {"state": "验证失败", "content": "请输入身份证后四位"}
        except Exception:
            driver.close()
            return {"state": "验证失败", "content": "短信认证失败"}

    # 短信验证-确定登录
    def Signin_By_Message(self, idcard, checkcode):
        # 短信认证浏览器对象
        flag = self.Get_CheckCode(idcard)['state']
        # 获取验证码成功
        if flag == '验证成功':
            driver = ShareDriver.driver
            if checkcode:
                # noinspection PyBroadException
                try:
                    driver.find_element(By.XPATH, "//*[@id='code']").send_keys(checkcode)
                    driver.find_element(By.XPATH, "//*[@id='sureClick']").click()
                    # print("点击了确定")
                    # 返回浏览器界面html
                    driver_response_content = BeautifulSoup(driver.page_source, "html.parser").select("#message")
                    # print(driver_response_content[0].get_text())
                    # 验证码验证成功
                    if driver_response_content[0].get_text():
                        ShareDriver.driver = driver
                        ShareDriver.login_user = BeautifulSoup(driver.page_source, "html.parser") \
                            .select('#J-header-logout > a.txt-primary.menu-nav-my-hd')[0].get_text()
                        return {"state": "登录成功", "content": "sucess"}
                    # 验证码验证失败
                    else:
                        # print("我执行了")
                        # 关闭浏览器，返回错误提示
                        driver.close()
                        return {"state": "验证失败", "content": driver_response_content[0].get_text()}
                except Exception as e:
                    print(e)
                    driver.close()
                    return {"state": "验证失败", "content": "短信认证失败"}


# 12306车票信息
class Ticket_info_12306:
    def __init__(self, start_station, end_station, start_date):
        # 出发地
        self.start_station = start_station
        # 目的地
        self.end_station = end_station
        # 出发时间
        self.start_date = start_date

    # 查询单程车票信息
    def search_ticketinfo(self, *sift):
        driver = ShareDriver.driver
        flag = ShareDriver.search_flag
        # print(driver.page_source)
        # 输入出发地、目的地、出发日期，点击查询
        # noinspection PyBroadException
        # 首次查询
        if flag:
            try:
                # 点击新冠提示-确定
                driver.find_element(By.LINK_TEXT, "确定").click()
                time.sleep(0.5)
            except Exception as e1:
                print("新冠提示错误：", e1)
            else:
                pass

            # 移动到车票
            ActionChains(driver, duration=500).move_to_element(
                driver.find_element(By.XPATH, "//*[@id='J-chepiao']/a")).perform()
            time.sleep(0.5)
            # 点击单程（购买）
            driver.find_element(By.XPATH, "//*[@id='megamenu-3']/div[1]/ul/li[1]/a").click()

            # 点击温馨提示-确认
            # noinspection PyBroadException
            try:
                warning_commit = driver.find_element(By.XPATH, "//*[@id='qd_closeDefaultWarningWindowDialog_id']")
                warning_commit.click()
                time.sleep(1)
            except Exception as e2:
                print("温馨提示错误：", e2)
            else:
                pass
        else:
            # 刷新页面，初始化筛选条件
            driver.refresh()

        # 出发地
        input_start_station = driver.find_element(By.XPATH, "//*[@id='fromStationText']")
        input_start_station.clear()
        input_start_station.click()
        input_start_station.send_keys(self.start_station)
        input_start_station.send_keys(Keys.ENTER)

        # 目的地
        input_endstation = driver.find_element(By.XPATH, "//*[@id='toStationText']")
        input_endstation.clear()
        input_endstation.click()
        input_endstation.send_keys(self.end_station)
        input_endstation.send_keys(Keys.ENTER)

        # 输入出发日期，注意时间格式必须为YYYY-MM-DD，否则会导致页面重定向（查询超时）指向非目标url
        input_date = driver.find_element(By.XPATH, "//*[@id='train_date']")
        input_date.clear()
        input_date.click()
        input_date.send_keys(self.start_date)
        input_date.send_keys(Keys.ENTER)

        # 查询-筛选条件
        # 有筛选条件,遍历筛选条件：0-未勾选，1-勾选
        # sift:(%d,%d,%d,%d,%d,%d)
        try:
            if sift:
                for check_id in range(6):
                    if sift[check_id]:
                        driver.find_element(By.CSS_SELECTOR, "#_ul_station_train_code>li:nth-of-type(" + str(check_id+1)
                                            + ")").click()
        except Exception as e3:
            print("筛选条件错误：", e3)
        else:
            pass
        bt_search = driver.find_element(By.XPATH, "//*[@id='query_ticket']")
        bt_search.click()
        # 首次查询结束，修改状态
        ShareDriver.search_flag = False
        time.sleep(2)
        # 解析车票信息
        # print("网页：", driver.page_source)
        ticketsinfo = self.ticketinfo_by_xpath(driver.page_source)

        # print("车次信息：", ticketsinfo)
        ShareDriver.driver = driver
        ShareTicket_Info.Tickets_num = len(ticketsinfo)
        ShareTicket_Info.Tickets_Info = ticketsinfo

    # 车票信息解析
    @staticmethod
    def ticketinfo_by_xpath(page):
        # 车次信息列表
        html_content = BeautifulSoup(page, 'html.parser')
        # 车次
        trains_id = []
        trains_num = html_content.select("#queryLeftTable .train>div>a")
        for train_num in trains_num:
            trains_id.append(train_num.get_text())
        # print(trains_id)

        # 出发站（始|过）-到达站（过|终）
        # [[出发站（始|过）-到达站（过|终）]，[出发站（始|过）-到达站（过|终）]，......]
        trains_stations = []
        trains_stations_s = []
        trains_stations_e = []
        stations = html_content.select("#queryLeftTable .cdz>strong")
        # print(stations)
        # print(type(stations[0]))
        # print(stations[0].get_text())
        # 出发站
        for station_num_s in range(0, len(stations), 2):
            # 是否为始发站
            if 'class' in str(stations[station_num_s]):
                trains_stations_s.append(stations[station_num_s].get_text() + '(始)')
            else:
                trains_stations_s.append(stations[station_num_s].get_text() + '(过)')
        # 目的地
        for station_num_e in range(1, len(stations) + 1, 2):
            # 是否为终点站
            if 'class' in str(stations[station_num_e]):
                trains_stations_e.append(stations[station_num_e].get_text() + '(终)')
            else:
                trains_stations_e.append(stations[station_num_e].get_text() + '(过)')
        if len(trains_stations_s) == len(trains_stations_e):
            # 合并出发地和目的地
            for station_num in range(len(trains_stations_s)):
                trains_stations.append([trains_stations_s[station_num], trains_stations_e[station_num]])
        else:
            print('解析出出发地与目的地不匹配')
        # print(trains_stations)

        # 出发时间-到达时间
        s_e_datetime_s = html_content.select("#queryLeftTable .cds>strong")
        # print(s_e_datetimes)
        s_e_datetimes = []
        s_datetimes = []
        e_datetimes = []
        # 出发时间
        for s_datetime_num in range(0, len(s_e_datetime_s), 2):
            s_datetimes.append(s_e_datetime_s[s_datetime_num].get_text())
        # 到达时间
        for e_datetime_num in range(1, len(s_e_datetime_s), 2):
            e_datetimes.append(s_e_datetime_s[e_datetime_num].get_text())
        if len(s_datetimes) == len(e_datetimes):
            # 合并出发时间和达到时间
            for datetime_num in range(len(s_datetimes)):
                s_e_datetimes.append([s_datetimes[datetime_num], e_datetimes[datetime_num]])
        else:
            print('解析出出发时间与到达时间不匹配')
        # print(s_e_datetimes)

        # 历时
        # 时间
        cost_times = html_content.select("#queryLeftTable .ls>strong")
        cost_times_isday = html_content.select("#queryLeftTable .ls>span")
        ls = []
        ls_t = []
        ls_d = []
        for cost_time in cost_times:
            ls_t.append(cost_time.get_text())
        # 是否当天到达
        for cost_time_isday in cost_times_isday:
            ls_d.append(cost_time_isday.get_text())
        if len(ls_t) == len(ls_d):
            for ls_num in range(len(ls_t)):
                ls.append([ls_t[ls_num], ls_d[ls_num]])
        # print(ls)

        # 商务座特等座
        bussiness_seats = html_content.select("#queryLeftTable tr>td:nth-of-type(2)")
        bussiness_seats_num = []
        for bussiness_seat in bussiness_seats:
            bussiness_seats_num.append(bussiness_seat.get_text())
        # print(bussiness_seats_num)

        # 一等座
        first_seats = html_content.select("#queryLeftTable tr>td:nth-of-type(3)")
        first_seats_num = []
        for first_seat in first_seats:
            first_seats_num.append(first_seat.get_text())
        # print(first_seats_num)

        # 二等座
        second_seats = html_content.select("#queryLeftTable tr>td:nth-of-type(4)")
        second_seats_num = []
        for second_seat in second_seats:
            second_seats_num.append(second_seat.get_text())
        # print(second_seats_num)

        # 高级软卧
        high_soft_berths = html_content.select("#queryLeftTable tr>td:nth-of-type(5)")
        high_soft_berths_num = []
        for high_soft_berth in high_soft_berths:
            high_soft_berths_num.append(high_soft_berth.get_text())
        # print(high_soft_berths_num)

        # 软卧
        soft_berths = html_content.select("#queryLeftTable tr>td:nth-of-type(6)")
        soft_berths_num = []
        for soft_berth in soft_berths:
            soft_berths_num.append(soft_berth.get_text())
        # print(soft_berths_num)

        # 动卧
        berths = html_content.select("#queryLeftTable tr>td:nth-of-type(7)")
        berths_num = []
        for berth in berths:
            berths_num.append(berth.get_text())
        # print(berths_num)

        # 硬卧
        second_berths = html_content.select("#queryLeftTable tr>td:nth-of-type(8)")
        second_berths_num = []
        for second_berth in second_berths:
            second_berths_num.append(second_berth.get_text())
        # print(second_berths_num)

        # 软座
        soft_seats = html_content.select("#queryLeftTable tr>td:nth-of-type(9)")
        soft_seats_num = []
        for soft_seat in soft_seats:
            soft_seats_num.append(soft_seat.get_text())
        # print(soft_seats_num)

        # 硬座
        seats = html_content.select("#queryLeftTable tr>td:nth-of-type(10)")
        seats_num = []
        for seat in seats:
            seats_num.append(seat.get_text())
        # print(second_seats_num)

        # 无座
        unfixed_seats = html_content.select("#queryLeftTable tr>td:nth-of-type(11)")
        unfixed_seats_num = []
        for unfixed_seat in unfixed_seats:
            unfixed_seats_num.append(unfixed_seat.get_text())
        # print(unfixed_seats_num)

        # 其他
        othering = html_content.select("#queryLeftTable tr>td:nth-of-type(12)")
        other_num = []
        for other in othering:
            other_num.append(other.get_text())
        # print(other_num)

        # 车票信息：车次-出发\到达站-出发\到达时间-历时-商务\特等座-一等座-二等座-高级软卧-软卧-动卧-硬卧-软座-硬座-无座-其他
        tickets_info = {}
        for num in range(len(trains_id)):
            tickets_info.update({trains_id[num]: [trains_stations[num], s_e_datetimes[num], ls[num],
                                                  bussiness_seats_num[num], first_seats_num[num], second_seats_num[num],
                                                  high_soft_berths_num[num], soft_berths_num[num], berths_num[num],
                                                  second_berths_num[num], soft_seats_num[num], seats_num[num],
                                                  unfixed_seats_num[num], other_num[num]]})

        # print(tickets_info)
        return tickets_info


